/*
 * Copyright 2008 Neil Bacon
 * Distributed under the terms of the Mozilla Public License version 1.1
 * or the GNU General Public License version 3.
 */

package nb.xadd.node.util;

import java.util.BitSet;

import nb.xadd.node.Node;
import nb.xadd.node.impl.AbstractDecisionNode;
import nb.xadd.node.impl.AbstractRealDecisionNode;
import nb.xadd.node.impl.BooleanDecisionNode;
import nb.xadd.node.impl.LinearValueNode;
import nb.xadd.node.impl.RealEqualDecisionNode;
import nb.xadd.node.impl.RealGreaterOrEqualDecisionNode;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * Log out of order nodes as errors
 * and some characteristics of the tree as info.
 * Count Nodes, track max depth and references to boolean and real variables.
 */
public class NodeLogger {

    private Log log = LogFactory.getLog(getClass());
    
    private int maxDepth = 0;
    private int count = 0;
    private BitSet realGreaterOrEqualVarSet = new BitSet();
    private BitSet realEqualVarSet = new BitSet();
    private BitSet booleanVarSet = new BitSet();
    
    private void clear() {
        maxDepth = 0;
        count = 0;
        realGreaterOrEqualVarSet.clear();
        realEqualVarSet.clear();
        booleanVarSet.clear();
    }
        
    private void accumulate(Node n, int depth) {
        ++count;
        if (depth > maxDepth) {
            maxDepth = depth;
        }
        if (n instanceof RealGreaterOrEqualDecisionNode) {
            realGreaterOrEqualVarSet.set(((AbstractRealDecisionNode) n).getRealVarIndex());
        } else if (n instanceof RealEqualDecisionNode) {
            realEqualVarSet.set(((AbstractRealDecisionNode) n).getRealVarIndex());
        } else if (n instanceof BooleanDecisionNode) {
            booleanVarSet.set(((BooleanDecisionNode) n).getBooleanVarIndex());
        } else if (n instanceof LinearValueNode) {
            double[] coeff = ((LinearValueNode) n).getCoeff();
            for (int realVarIndex = 0; realVarIndex < coeff.length; ++realVarIndex) {
                if (coeff[realVarIndex] != 0.0) {
                    realGreaterOrEqualVarSet.set(realVarIndex);
                }
            }
        }
    }

    public void log(Node n) {
        clear();
        walk(n, 1);
        log.info("count = " + count + ", maxDepth = " + maxDepth + ", booleanVars = " + booleanVarSet
                + ", realEqualVars = " + realEqualVarSet
                + ", realGreaterOrEqualVars = " + realGreaterOrEqualVarSet);
    }

    private void walk(Node n, int depth) {
        if (log.isInfoEnabled()) {
            accumulate(n, depth);
        }
        if (n instanceof AbstractDecisionNode) {
            AbstractDecisionNode dn = (AbstractDecisionNode) n;
            if (dn.compareTo(dn.getTrueChild()) >= 0) {
                log.error("Incorrect order: parent: " + dn.description() + " trueChild: " + dn.getTrueChild().description());
            }
            if (dn.compareTo(dn.getFalseChild()) >= 0) {
                log.error("Incorrect order: parent: " + dn.description() + " falseChild: " + dn.getFalseChild().description());
            }
            walk(dn.getTrueChild(), depth + 1);
            walk(dn.getFalseChild(), depth + 1);
        }
    }

}
